#ifndef KINECTDIALOG_H
#define KINECTDIALOG_H

#ifdef KINECT

#include <QApplication>
#include <QDialog>
#include <QSharedPointer>

#include "global.h"
#include "Kinect/kinect.h"
#include "Kinect/kinectskeleton.h"
#include "troypose.h"
#include "userskeleton.h"

#ifdef WIIMOTE
#include "GUI/wiimote/wiimotewidget.h"
class WiimoteStatusDialog;
#endif

/**
  * @brief The base class for all Dialogs that require tracking a user via Kinect. It also implements
  * the base functionality and allows subclasses to connect to a wiimote for input.
  *
  * The main functionality of this class is automating calibration. As soon as one of these dialogs is shown,
  * the Kinect is checked, and if no calibrated user is found, the calibration dialog opens. Also, if at any point
  * during the lifetime of the KinectDialog the user skeleton is lost, the calibration window should once again
  * open.
  *
  * The second thing this class takes care of if creating the KinectSkeleton tracker on the Kinect object, and
  * then linking it to a TroyMapper automatically. Subclasses can use the getTroyMapper(), getKinect() and
  * other methods if they need access to these objects (for configuration, etc.). Each time a new frame is
  * seen, the kinectKeyFrame() slot is called, where either the passed in keyFrame or calls to getKeyFrame()
  * and getTroyState() can tell the subclass what position the user is in presently.
  */
class KinectDialog : public QDialog
#ifdef WIIMOTE
    , public WiimoteWidget
#endif
{
private:
    Q_OBJECT
    QSharedPointer<TroyPose> _currentState;
    kinect::Kinect* _kinect;
    kinect::KinectSkeleton* _skeleton;

public:
    explicit KinectDialog(kinect::Kinect* kinect, QWidget *parent = 0);
    ~KinectDialog();

    /**
      * @brief Static function to calibrate the kinect and find a user skeleton. Opens a new
      * KinectCalibrationDialog to do the calibration.
      *
      * @param kinect
      * @param parent
      */
    static bool calibrate(kinect::Kinect* kinect, QWidget *parent = 0);
#ifdef WIIMOTE
    /**
      * @brief Static function to calibrate the kinect and find a user skeleton. Opens a new
      * KinectCalibrationDialog to do the calibration. This version enables Wiimote control
      * to be linked to the new Dialog window.
      *
      * @param kinect
      * @param wiimoteDialog
      * @param parent
      */
    static bool calibrate(kinect::Kinect *kinect, WiimoteStatusDialog* wiimoteDialog, QWidget *parent = 0);
#endif
protected slots:
    /**
      * @brief Slot callback that calls the static calibration function, using the kinect given in the
      * constructor, and this as parent.
      */
    void calibrateKinect();
    /**
      * @brief Slot called when the Kinect is calibrated, to be extended by base classes.
      */
    virtual void kinectCalibrated() {}
    /**
      * @brief Slot to be reimplemented by subclasses who wish to do something on every new frame of data.
      * By the time this is called, the new troy state and keyframe have been loaded in.
      *
      * @param keyFrame
      */
    //virtual void kinectKeyFrame(MotionKeyFrame keyFrame) {}
    virtual void receiveTroyPose(QSharedPointer<TroyPose> pose) {}
protected:
    /**
      * @brief Gets the Kinect object associated with this dialog.
      */
    kinect::Kinect* getKinect();
    /**
      * @brief Gets the most recently seen skeleton data from the Kinect as a TroyState object.
      * Note that this will return null before any skeleton data has been seen. As soon as a kinectKeyFrame()
      * callback has been fired, it should always return a valid object.
      */
    //TroyState* getTroyState();
    QSharedPointer<TroyPose> getTroyPose();
    /**
      * @brief Gets the KinectSkeleton object used to track the skeleton on the Kinect.
      */
    kinect::KinectSkeleton* getKinectSkeleton();
    /**
      * @brief Gets the most recent skeletal data received from the Kinect in the form of a MotionKeyFrame
      * with an invalid timestamp. This will always return a value of some sort, but until a
      * kinectKeyFrame() callback has been made, there is no guarantee that this will be any good.
      */
    //MotionKeyFrame getKeyFrame();
    /**
      * @brief Pseudo-maximizes the window via resizing, so that it fills the whole availible desktop window,
      * but still allows the user to resize each edge without having to unmaximize. Handy for KinectDialogs
      * so that the user can see the monitor from a decent distance.
      */
    void maximizeSize();

private slots:
    void processSkeleton(QSharedPointer<UserSkeleton> skeleton);

};

#endif // KINECT

#endif // KINECTDIALOG_H
